package cc.decat.escript.runtime;

import cc.decat.escript.codegen.SymbolTable;
import cc.decat.escript.codegen.UpVarInfo;
import cc.decat.escript.object.ESObject;

public class Env {
    private final Env parent;
    private final ESObject[] localObjs;
    private final ESObject[] upObjs;
    private final SymbolTable symbolTable;

    public Env(Env parent, SymbolTable symbolTable) {
        this.parent = parent;
        this.symbolTable = symbolTable;
        this.localObjs = new ESObject[symbolTable.getLocalVarInfoList().size()];
        this.upObjs = new ESObject[symbolTable.getUpVarInfoList().size()];
        if(parent != null) {
            fillUpVar();
        }
    }

    private void fillUpVar() {
        for(UpVarInfo upVarInfo : symbolTable.getUpVarInfoList()) {
            if(upVarInfo.isParentLocal()) {
                this.setUpObj(upVarInfo.getIndex(), parent.getLocalObj(upVarInfo.getIndexOfParent()));
            } else {
                this.setUpObj(upVarInfo.getIndex(), parent.getUpObj(upVarInfo.getIndexOfParent()));
            }
        }
    }

    public ESObject getLocalObj(int index) {
        return localObjs[index];
    }

    public ESObject getUpObj(int index) {
        return upObjs[index];
    }

    public void setLocalObj(int index, ESObject val) {
        this.localObjs[index] = val;
    }

    public void setUpObj(int index, ESObject val) {
        this.upObjs[index] = val;
        Env parentEnv = this.parent;
        if(parentEnv == null) {
            return;
        }
        UpVarInfo upVarInfo = this.symbolTable.getUpVarInfoList().get(index);
        if(upVarInfo.isParentLocal()) {
            parentEnv.setLocalObj(upVarInfo.getIndexOfParent(), val);
        } else {
            parentEnv.setUpObj(upVarInfo.getIndexOfParent(), val);
        }
    }

    public SymbolTable getSymbolTable() {
        return symbolTable;
    }

    public Env getParent() {
        return parent;
    }
}
